﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.Serialization;
using System.Globalization;

using System.Data;

namespace LibraryManagement
{
    #region Base Classes

    #region DomainData class
    /// <summary>
    /// The base class for all <c>Domain</c> data classes.
    /// </summary>
    [Serializable]
    public abstract class DomainData : ICloneable
    {
        #region Nested Property Struct
        /// <summary>
        /// Defines a property within the <c>Domain Aggregate</c>.
        /// </summary>
        [Serializable]
        internal class Property
        {
            #region Fields
            private DomainData _parent;
            #endregion

            #region Constructors
            /// <summary>
            /// Initializes a new instance of the <see cref="Property"/> class.
            /// </summary>
            /// <param name="propertyName">Name of the property.</param>
            /// <param name="columnName">Name of the column.</param>
            /// <param name="dataType">Type of the data.</param>
            /// <param name="defaultValue">The default value.</param>
            /// <param name="isElement">if set to <c>true</c> [is element].</param>
            /// <param name="parent">The parent.</param>
            /// <param name="isXml">if set to <c>true</c> [is XML].</param>
            /// <param name="isKey">if set to <c>true</c> [is key].</param>
            internal Property(String propertyName, String columnName, Type dataType, Object defaultValue, Boolean isElement, DomainData parent, Boolean isXml, Boolean isKey)
            {
                if (String.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
                _name = propertyName;
                _columnName = columnName;
                _propDataType = dataType;
                _defaultValue = defaultValue;
                _value = defaultValue;
                _isElement = isElement;
                _parent = parent;
                _isXml = isXml && dataType == typeof(String);
                _isKey = isKey;
            }
            #endregion

            #region Properties
            /// <summary>
            /// Gets or sets the Property data type which could be
            /// an array.
            /// </summary>
            /// <value>The type of the data.</value>
            public Type PropertyDataType
            {
                get { return _propDataType; }
            }private Type _propDataType;

            /// <summary>
            /// Gets the obsolute data type which could be different then
            /// the <see cref="PropertyDataType"/>.
            /// <value>The individual array item type or the Property type if
            /// the property is not an array.</value>
            private Type DataType
            {
                get
                {
                    if (!_propDataType.IsArray)
                    {
                        return _propDataType;
                    }
                    else
                    {
                        if (_dataType == null) _dataType = GetArrayItemType(_propDataType);
                        return _dataType;
                    }
                }
            }private Type _dataType;
            /// <summary>
            /// Gets or sets the default value.
            /// </summary>
            /// <value>The default value.</value>
            public Object DefaultValue
            {
                get { return _defaultValue; }
            }private Object _defaultValue;
            /// <summary>
            /// Gets or sets the name.
            /// </summary>
            /// <value>The name.</value>
            public String Name
            {
                get { return _name; }
            }private String _name;
            /// <summary>
            /// Gets the name of the  database column
            /// containing this property value.
            /// </summary>
            /// <value>The name of the column.</value>
            public String ColumnName
            {
                get { return _columnName; }
            }private String _columnName;
            /// <summary>
            /// Gets or sets the value.
            /// </summary>
            /// <value>The value.</value>
            public Object Value
            {
                get { return _value; }
                set
                {
                    if (!Object.Equals(_value, value) && _parent != null) _parent.IsDirty = true;
                    _value = value;
                }
            }private Object _value;
            /// <summary>
            /// Gets a value indicating whether this instance is an
            /// element node or an attribute node.
            /// </summary>
            /// <value>
            /// 	<c>true</c> if this instance is element; otherwise, <c>false</c>.
            /// </value>
            public Boolean IsElement
            {
                get { return _isElement; }
            }private Boolean _isElement;

            /// <summary>
            /// Gets a value indicating whether this instance is XML.
            /// </summary>
            /// <value><c>true</c> if this instance is XML; otherwise, <c>false</c>.</value>
            public Boolean IsXml
            {
                get { return _isXml; }
            }private Boolean _isXml;
            /// <summary>
            /// Gets a value indicating whether this instance has a value.
            /// </summary>
            /// <value><c>true</c> if this instance has a value; otherwise, <c>false</c>.</value>
            public Boolean HasValue
            {
                get { return Value != null; }
            }
            /// <summary>
            /// Gets a value indicating whether this instance is the key
            /// column for the entity.
            /// </summary>
            /// <value><c>true</c> if this instance is key; otherwise, <c>false</c>.</value>
            public Boolean IsKey
            {
                get { return _isKey; }
            }private Boolean _isKey;
            /// <summary>
            /// Gets the converter.
            /// </summary>
            /// <value>The converter.</value>
            private TypeConverter Converter
            {
                get
                {
                    if (_converter == null)
                    {
                        _converter = GetConverter(DataType);
                    }
                    return _converter;
                }
            }
            [NonSerialized]
            private TypeConverter _converter;
            #endregion

            #region Methods
            /// <summary>
            /// Loads from string.
            /// </summary>
            /// <param name="propValue">The prop value.</param>
            public void ConvertFromString(String propValue)
            {
                if (!String.IsNullOrEmpty(propValue))
                {
                    if (PropertyDataType.IsArray)
                        Value = DeserializeArray(Converter, DataType, propValue);
                    else
                        Value = Converter.ConvertFromString(null, System.Globalization.CultureInfo.CurrentCulture, propValue);
                }

            }
            /// <summary>
            /// Converts the property value to a string.
            /// </summary>
            /// <returns></returns>
            public String ConvertToString()
            {
                String propertyValue = String.Empty;
                if (HasValue)
                {
                    if (PropertyDataType.IsArray)
                    {
                        propertyValue = SerializeArray(Converter, (Array)Value);
                    }
                    else
                    {
                        propertyValue = Converter.ConvertToString(null, System.Globalization.CultureInfo.CurrentCulture, Value);
                    }
                }
                return propertyValue;
            }
            #endregion

            #region Static Methods
            /// <summary>
            /// Template to serialize a numeric array into a comma delimited
            /// string.
            /// </summary>
            /// <param name="converter">The converter.</param>
            /// <param name="data">The numeric array to serialize.</param>
            /// <returns>
            /// Comma delimited string representation of the array.
            /// </returns>
            private static String SerializeArray(TypeConverter converter, Array data)
            {
                StringBuilder builder = new StringBuilder();
                if (data != null && data.Length > 0)
                {
                    for (Int32 idx = 0; idx < data.Length; idx++)
                    {
                        if (idx > 0) builder.Append('~');
                        builder.Append(converter.ConvertToString(data.GetValue(idx)));
                    }
                }
                return builder.ToString();
            }
            /// <summary>
            /// Template to deserialize a comma delimited string of numeric values
            /// into an array of the given type.
            /// </summary>
            /// <param name="converter">The converter.</param>
            /// <param name="arrayItemType">Type of the array item.</param>
            /// <param name="data">The comma delimited string representation
            /// of the data array.</param>
            /// <returns>An array.</returns>
            private static Array DeserializeArray(TypeConverter converter, Type arrayItemType, String data)
            {
                System.Collections.ArrayList array = new System.Collections.ArrayList();
                if (!String.IsNullOrEmpty(data))
                {
                    String[] values = data.Split('~');
                    foreach (String value in values)
                    {
                        //Object item;
                        //if (TryParse(itemType, value, out item)) array.Add(item);
                        Object item = converter.ConvertFromString(null, System.Globalization.CultureInfo.CurrentCulture, value);
                        if (item != null) array.Add(item);
                    }
                }
                return array.ToArray(arrayItemType);
            }
            /// <summary>
            /// Gets the converter.
            /// </summary>
            /// <param name="dataType">Type of the data.</param>
            /// <returns></returns>
            private static TypeConverter GetConverter(Type dataType)
            {
                TypeConverter converter = null;
                if (dataType.IsEnum)
                    converter = new System.Configuration.GenericEnumConverter(dataType);
                else
                    converter = System.ComponentModel.TypeDescriptor.GetConverter(dataType);
                return converter;
            }
            /// <summary>
            /// Gets the type of the array item.
            /// </summary>
            /// <param name="arrayType">Type of the array.</param>
            /// <returns></returns>
            private static Type GetArrayItemType(Type arrayType)
            {
                Type type = typeof(Object);
                switch (arrayType.Name)
                {
                    case "Int32[]":
                        type = typeof(Int32);
                        break;
                    case "Int16[]":
                        type = typeof(Int16);
                        break;
                    case "Double[]":
                        type = typeof(Double);
                        break;
                    case "Single[]":
                        type = typeof(Single);
                        break;
                    case "Guid[]":
                        type = typeof(Guid);
                        break;
                    case "DateTime[]":
                        type = typeof(DateTime);
                        break;
                    case "Boolean[]":
                        type = typeof(Boolean);
                        break;
                    case "Decimal[]":
                        type = typeof(Decimal);
                        break;
                    case "String[]":
                        type = typeof(String);
                        break;
                    default:
                        throw new NotSupportedException("Type array specified is not supported!");
                }
                return type;
            }
            #endregion

        }
        #endregion

        #region Fields
        protected const String NameSpace = @"http://schemas.InsuranceTechnologies.com/PGL";
        private Dictionary<String, Property> _properties;
        private List<Property> _keys;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="T:DomainData"/> class.
        /// </summary>
        protected DomainData()
        {
            _properties = new Dictionary<string, Property>();
            _keys = new List<Property>(5);
            _isNew = true;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets a value indicating whether property values have changed
        /// since the instance was created or loaded.
        /// </summary>
        /// <value><c>true</c> if this instance is dirty; otherwise, <c>false</c>.</value>
        public Boolean IsDirty
        {
            get { return _isDirty; }
            internal set
            {
                _isDirty = value;
                //cascade down through the hierarchy setting
                //each parent as dirty.
                if (_parent != null) _parent.IsDirty = value;
            }
        }private Boolean _isDirty;
        /// <summary>
        /// Gets a value indicating whether this instance is new.
        /// </summary>
        /// <value><c>true</c> if this instance is new; otherwise, <c>false</c>.</value>
        public Boolean IsNew
        {
            get { return _isNew; }
            set
            {
                _isNew = value;
                if (_isNew)
                    _entityState = EntityState.New;
                else
                    _entityState = EntityState.Updated;
            }
        }private Boolean _isNew;
        /// <summary>
        /// Gets or sets the parent.
        /// </summary>
        /// <value>The parent.</value>
        internal DomainData Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }private DomainData _parent;

        /// <summary>
        /// Gets or sets the state of the entity.
        /// </summary>
        /// <value>The state of the entity.</value>
        public EntityState EntityState
        {
            get { return _entityState; }
            set { _entityState = value; }
        }private EntityState _entityState;

        #endregion

        #region  Internal Methods
        /// <summary>
        /// Loads the configuration XML.
        /// </summary>
        /// <param name="reader">The reader.</param>
        internal virtual Boolean LoadDataXml(XmlReader reader)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            Boolean consumed = false;
            if (reader.NodeType == XmlNodeType.Element)
            {
                using (XmlReader entityReader = reader.ReadSubtree())
                {
                    if (entityReader.MoveToContent() == XmlNodeType.Element)
                    {
                        if (entityReader.MoveToFirstAttribute())
                        {
                            consumed = true;
                            do
                            {
                                if (_properties.ContainsKey(entityReader.Name))
                                {
                                    Property prop = _properties[entityReader.Name];
                                    if (!prop.IsElement)
                                    {
                                        prop.ConvertFromString(entityReader.Value);
                                        _properties[entityReader.Name] = prop;
                                    }
                                }
                            } while (entityReader.MoveToNextAttribute());
                        }
                        entityReader.Read();
                        //move through the elements
                        while (entityReader.MoveToContent() == XmlNodeType.Element)
                        {
                            consumed = true;
                            if (_properties.ContainsKey(entityReader.Name))
                            {
                                Property prop = _properties[entityReader.Name];
                                if (prop.IsElement)
                                {
                                    if (IsChildObject(prop.PropertyDataType))
                                    {
                                        //create the object.
                                        DomainData child = CreateChildObject(entityReader.Name);
                                        child.Parent = this;
                                        prop.Value = child;
                                        _properties[entityReader.Name] = prop;
                                        if (child != null) child.LoadDataXml(entityReader);
                                    }
                                    else
                                    {
                                        prop.ConvertFromString(entityReader.ReadString());
                                        _properties[entityReader.Name] = prop;
                                    }
                                    entityReader.Read();
                                }
                                else
                                {
                                    entityReader.Skip();
                                }
                            }
                            else
                            {
                                entityReader.Skip();
                            }
                        }
                    }
                }
            }
            IsNew = !consumed;
            return consumed;
        }
        /// <summary>
        /// Writes the entity XML.
        /// </summary>
        /// <param name="writer">The writer.</param>
        internal virtual void WriteDataXml(XmlWriter writer)
        {
            if (writer == null) throw new ArgumentNullException("writer");
            //writer out the attributes for the root element
            foreach (Property prop in _properties.Values)
            {
                if (!prop.IsElement && prop.HasValue)
                {
                    String propertyValue = prop.ConvertToString();
                    writer.WriteAttributeString(prop.Name, propertyValue);
                }
            }
            //write out the child elements
            foreach (Property prop in _properties.Values)
            {
                if (prop.IsElement && prop.HasValue)
                {
                    writer.WriteStartElement(prop.Name);
                    if (IsChildObject(prop.PropertyDataType))
                    {
                        DomainData child = prop.Value as DomainData;
                        child.WriteDataXml(writer);
                    }
                    else
                    {
                        String propertyValue = prop.ConvertToString();
                        if (prop.IsXml)
                            writer.WriteCData(propertyValue);
                        else
                            writer.WriteString(propertyValue);
                    }
                    writer.WriteEndElement();
                }
            }
        }
        /// <summary>
        /// Gets the properties.
        /// </summary>
        /// <returns></returns>
        internal List<Property> GetProperties()
        {
            return new List<Property>(_properties.Values);
        }
        /// <summary>
        /// Gets the name of the element.
        /// </summary>
        /// <returns></returns>
        internal protected virtual String GetElementName()
        {
            return GetType().Name;
        }
        #endregion

        #region Protected Methods
        /// <summary>
        /// Creates the child object.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        protected virtual DomainData CreateChildObject(String propertyName)
        {
            //default implementation is to create the child object using
            //the type defined in the property.
            DomainData entity = null;
            if (_properties.ContainsKey(propertyName))
            {
                Property prop = _properties[propertyName];
                try
                {
                    entity = Activator.CreateInstance(prop.PropertyDataType) as DomainData;
                }
                catch (Exception) { }
            }
            return entity;
        }
        /// <summary>
        /// Adds the property.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="dataType">Type of the data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <param name="isKey">if set to <c>true</c> [is key].</param>
        protected void AddProperty(String propertyName, String columnName, Type dataType, Object defaultValue, Boolean isKey)
        {
            //delegate to overload method.
            AddProperty(propertyName, columnName, dataType, defaultValue, IsChildObject(dataType), false, isKey);
        }
        /// <summary>
        /// Adds the property.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="dataType">Type of the data.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <param name="isElement">if set to <c>true</c> [is element].</param>
        /// <param name="isXml">if set to <c>true</c> [is XML].</param>
        /// <param name="isKey">if set to <c>true</c> [is key].</param>
        protected void AddProperty(String propertyName, String columnName, Type dataType, Object defaultValue, Boolean isElement, Boolean isXml, Boolean isKey)
        {
            Property prop = new Property(propertyName, columnName, dataType, defaultValue, isElement, this, isXml, isKey);
            if (!_properties.ContainsKey(propertyName))
                _properties.Add(prop.Name, prop);
            else
                _properties[prop.Name] = prop;
            if (prop.IsKey) _keys.Add(prop);
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Loads the XML.
        /// </summary>
        /// <param name="reader">The reader.</param>
        public void LoadXml(XmlReader reader)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            if (reader.Read())
            {
                //move to beginning of content
                if (reader.MoveToContent() != XmlNodeType.Element) throw new XmlException("Invalid Xml format.");
                //load up the entity.
                LoadDataXml(reader);
                AcceptChanges();
            }
        }
        /// <summary>
        /// Writes the XML.
        /// </summary>
        /// <param name="writer">The writer.</param>
        public void WriteXml(XmlWriter writer)
        {
            if (writer == null) throw new ArgumentNullException("writer");
            //start document
            writer.WriteStartDocument(true);
            //writer out the contents of the entity.
            writer.WriteStartElement(GetType().Name);
            WriteDataXml(writer);
            //finish up the document
            writer.WriteEndElement();
            writer.WriteEndDocument();
            AcceptChanges();
        }
        /// <summary>
        /// Accepts the changes.
        /// </summary>
        public void AcceptChanges()
        {
            //tranverse to parent and have the parent
            //tranverse back up the children, calling internalAcceptChanges()
            if (_parent != null)
                _parent.AcceptChanges();
            else
                InternalAcceptChanges();
        }
        /// <summary>
        /// Sets the value.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="value">The value.</param>
        public void SetValue(String propertyName, Object value)
        {
            if (!_properties.ContainsKey(propertyName)) throw new ArgumentException("propertyName");
            Property prop = _properties[propertyName];
            prop.Value = value;
            _properties[propertyName] = prop;
        }
        /// <summary>
        /// Gets the value.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        public Object GetValue(String propertyName)
        {
            if (!HasProperty(propertyName)) throw new ArgumentException(String.Format("'{0}' property doesn't exist.", propertyName));
            return GetPropertyValue(this, propertyName);
        }
        /// <summary>
        /// Gets the value by using a DataBinding expression syntax.
        /// </summary>
        /// <param name="propertyExpression">The DataBinding expression.</param>
        /// <returns>Value of the property or null if it doesn't exist.</returns>
        public Object GetValueByExpression(String propertyExpression)
        {
            if (String.IsNullOrEmpty(propertyExpression)) throw new ArgumentNullException("propertyExpression");
            String[] expressionList = propertyExpression.Split('.');
            return GetValue(expressionList);
        }
        /// <summary>
        /// Gets the value.
        /// </summary>
        /// <param name="expressionList">The expression list.</param>
        /// <returns></returns>
        private Object GetValue(String[] expressionList)
        {
            Object propertyValue = this;
            if (expressionList == null || expressionList.Length == 0) throw new ArgumentException("Invalid Property Accessor syntax.");
            foreach (String expression in expressionList)
            {
                if (!String.IsNullOrEmpty(expression))
                {
                    if (expression.StartsWith("["))
                    {
                        //array or collection
                        propertyValue = GetIndexedPropertyValue(propertyValue, expression);
                    }
                    else
                    {
                        //property
                        propertyValue = GetPropertyValue(propertyValue as DomainData, expression);
                    }
                }
                if (propertyValue == null) break;
            }
            return propertyValue;
        }
        /// <summary>
        /// Gets the property value.
        /// </summary>
        /// <param name="propertyValue">The property value.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns></returns>
        private Object GetPropertyValue(DomainData propertyValue, String propertyName)
        {
            Object value = null;
            if (propertyValue != null && propertyValue.HasProperty(propertyName))
            {
                Property prop = propertyValue._properties[propertyName];
                //if the value is null and the type is a configurationdata, try lazy creating
                //the object by calling the create child object.
                if (prop.Value == null && IsChildObject(prop.PropertyDataType))
                {
                    DomainData propEntity = propertyValue.CreateChildObject(prop.Name);
                    propEntity.Parent = propertyValue;
                    prop.Value = propEntity;
                    propertyValue._properties[propertyName] = prop;
                }
                value = prop.Value;
            }
            return value;
        }

        /// <summary>
        /// Gets the indexed property value.
        /// </summary>
        /// <param name="propertyValue">The property value.</param>
        /// <param name="expression">The expression.</param>
        /// <returns></returns>
        private static Object GetIndexedPropertyValue(Object propertyValue, String expression)
        {
            String priorProperty = String.Empty;
            if (propertyValue == null) throw new ArgumentException(String.Format("'{0}' is not an array or a DomainDataCollection.", priorProperty));
            Int32 pos = expression.IndexOf(']');
            if (pos < 0) throw new ArgumentException(String.Format("Invalid indexing expression."));
            Int32 idx = 0;
            if (!Int32.TryParse(expression.Substring(1, pos - 1), NumberStyles.Integer, CultureInfo.InvariantCulture, out idx)) throw new ArgumentException(String.Format("Invalid indexing expression."));
            Array temp = propertyValue as Array;
            if (temp != null)
            {
                propertyValue = temp.GetValue(idx);
            }
            else
            {
                //try collection
                IList<DomainAggregate> list = propertyValue as IList<DomainAggregate>;
                if (list == null) throw new ArgumentException(String.Format("'{0}' is not an array or a DomainDataCollection.", priorProperty));
                propertyValue = list[idx];
            }
            return propertyValue;
        }
        /// <summary>
        /// Determines whether the DomainData instance contains
        /// the specified property.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>
        /// 	<c>true</c> if the specified property name exists; otherwise, <c>false</c>.
        /// </returns>
        public Boolean HasProperty(String propertyName)
        {
            return _properties.ContainsKey(propertyName);
        }
        /// <summary>
        /// Gets the column map.
        /// </summary>
        /// <returns></returns>
        /// <remarks>Maps the Stored Procedure parameter names, or the Table Column Name to the
        /// property name within the entity by using the information supplied to the
        /// <c>AddProperty</c> method within the ctor.</remarks>
        public Dictionary<String, String> GetColumnMap()
        {
            Dictionary<String, String> map = new Dictionary<string, string>(_properties.Count);
            foreach (Property prop in _properties.Values)
            {
                if (!String.IsNullOrEmpty(prop.ColumnName))
                    map.Add(prop.ColumnName.ToUpper(), prop.Name);
            }
            return map;
        }
        /// <summary>
        /// Loads the data using the contents of the supplied
        /// <see cref="System.Data.IDataReader"/> and the ColumnName property
        /// of the <see cref="T:Property"/> class to load the entity.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns><c>true</c> if the entity was loaded; <c>false</c> otherwise.</returns>
        public virtual Boolean LoadData(System.Data.IDataReader reader)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            Boolean loaded = false;
            if (reader != null && reader.Read())
            {
                //we have data, load the properties using the columnName of each
                //property as the map into the reader's columns.
                List<String> fields = new List<string>(reader.FieldCount);
                for (Int32 idx = 0; idx < reader.FieldCount; idx++)
                {
                    fields.Add(reader.GetName(idx).ToUpper());
                }
                foreach (Property prop in _properties.Values)
                {
                    if (!String.IsNullOrEmpty(prop.ColumnName))
                    {
                        Int32 pos = fields.IndexOf(prop.ColumnName.ToUpper());
                        if (pos > -1 && !reader.IsDBNull(pos)) prop.Value = reader.GetValue(pos);
                    }
                }
                loaded = true;
            }
            AcceptChanges();
            IsNew = !loaded;
            return loaded;
        }
        #endregion

        #region Static Methods
        /// <summary>
        /// Converts the list of <see cref="T:DomainEntity"/> types to
        /// a <see cref="T:DataTable"/> where the property names within the entity
        /// are used to create the columns within the <c>DataTable</c>.  The property
        /// values of each entity are mapped to the row and column within the <c>DataTable</c>.
        /// </summary>
        /// <param name="entities">The entities.</param>
        /// <param name="tableName">Name of the table.</param>
        /// <returns></returns>
        /// <remarks>Any child objects are ignored and array properties (e.g. Int32[]) are
        /// converted to csv (comma separated values).</remarks>
        public static DataTable ConvertToDataTable<T>(IList<T> entities, String tableName) where T : DomainData, new()
        {
            DataTable dt = new DataTable(tableName);
            //each column in the datatable will map to the property of the
            //entity structure.  any child object properties will be ignored and
            //array properties will be stored as csv.
            T temp = new T();
            var props = from x in temp._properties
                        where !IsChildObject(x.Value.PropertyDataType)
                        select x.Value;
            foreach (Property prop in props)
            {
                if (prop.PropertyDataType.IsArray)
                    dt.Columns.Add(prop.Name, typeof(String));
                else
                    dt.Columns.Add(prop.Name, prop.PropertyDataType);
            }
            //now loop through the entities and add to each column row
            //in the datatable the value of the properties.
            if (entities != null && entities.Count > 0)
            {
                foreach (DomainData entity in entities)
                {
                    DataRow row = dt.NewRow();
                    foreach (Property prop in props)
                    {
                        String propName = prop.Name;
                        if (entity.HasProperty(propName))
                        {
                            if (prop.PropertyDataType.IsArray)
                                //convert to csv string
                                row[propName] = entity._properties[propName].ConvertToString();
                            else
                                //normal
                                row[propName] = entity.GetValue(propName);
                        }
                    }
                    //add in the row
                    dt.Rows.Add(row);
                }
                dt.AcceptChanges();
            }
            return dt;
        }
        #endregion

        #region ICloneable Members
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public virtual object Clone()
        {
            DomainData cloned = Activator.CreateInstance(GetType()) as DomainData;
            foreach (Property prop in _properties.Values)
            {
                cloned.AddProperty(prop.Name, prop.ColumnName, prop.PropertyDataType, prop.DefaultValue, prop.IsElement, prop.IsXml, prop.IsKey);
                if (IsChildObject(prop.PropertyDataType))
                {
                    if (prop.HasValue)
                    {
                        DomainData child = prop.Value as DomainData;
                        cloned.SetValue(prop.Name, child.Clone());
                    }
                }
                else
                {
                    cloned.SetValue(prop.Name, prop.Value);
                }
            }
            return cloned;
        }
        #endregion

        #region Base Overrides
        /// <summary>
        /// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"></see> is 
        /// suitable for use in hashing algorithms and data structures like a hash table.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override int GetHashCode()
        {
            //build a hashcode consisting of the type name and
            //any properties identified as key elements.
            StringBuilder builder = new StringBuilder();
            builder.Append(GetType().FullName);
            foreach (Property prop in _keys)
            {
                if (prop.IsKey)
                {
                    builder.AppendFormat(":{0}:{1}", prop.Name, prop.Value == null ? "NULL" : prop.Value.ToString());
                }
            }
            return builder.ToString().GetHashCode();
        }
        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            else
                return obj.GetHashCode() == GetHashCode();
        }
        #endregion

        #region IXPathNavigable Members


        #endregion

        #region Private Helper Methods
        /// <summary>
        /// Accepts all changes on the current instance and
        /// any nested child <c>DomainData</c> instances.
        /// </summary>
        private void InternalAcceptChanges()
        {
            foreach (Property prop in _properties.Values)
            {
                if (IsChildObject(prop.PropertyDataType) && prop.Value != null)
                {
                    DomainData data = prop.Value as DomainData;
                    data.InternalAcceptChanges();
                }
            }
            IsDirty = false;
        }
        /// <summary>
        /// Determines whether the specified type is a <see cref="T:ConfigurationData"/>
        /// derived class.
        /// </summary>
        /// <param name="dataType">Type of the data.</param>
        /// <returns>
        /// 	<c>true</c> if the type is a child object; otherwise, <c>false</c>.
        /// </returns>
        private static Boolean IsChildObject(Type dataType)
        {
            return dataType.IsSubclassOf(typeof(DomainData)) || dataType.Equals(typeof(DomainData));
        }
        #endregion
    }
    #endregion

    #region DomainAggregate class
    /// <summary>
    /// The base class for all <c>Domain</c> data classes except
    /// collections.  Collections must derive from <see cref="T:DomainDataCollection"/>
    /// </summary>
    [Serializable]
    public class DomainAggregate : DomainData
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DomainAggregate"/> class.
        /// </summary>
        public DomainAggregate() : base() { }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the <see cref="T:Object"/> with the specified name.
        /// </summary>
        /// <value></value>
        public Object this[String name]
        {
            get { return GetValue(name); }
            set { SetValue(name, value); }
        }

        #endregion
    }
    #endregion

    #region DomainDataCollection class
    /// <summary>
    /// Base class for custom collection for <c>Domain</c> data classes.
    /// </summary>
    /// <typeparam name="TElement"></typeparam>
    [Serializable]
    public class DomainDataCollection<TElement> : DomainData, IList<TElement>, IListSource where TElement : DomainData, new()
    {
        #region Fields
        private DomainDataList<TElement> _list;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="T:DomainDataCollection&lt;TElement&gt;"/> class.
        /// </summary>
        public DomainDataCollection()
            : base()
        {
            _list = new DomainDataList<TElement>(this);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the <see cref="T:TElement"/> with the specified idx.
        /// </summary>
        /// <value></value>
        public TElement this[Int32 index]
        {
            get { return (TElement)_list[index]; }
            set
            {
                _list[index] = value;
                IsDirty = true;
            }
        }
        #endregion

        #region Base Overrides
        /// <summary>
        /// Loads the entity XML.
        /// </summary>
        /// <param name="reader">The reader.</param>
        internal override Boolean LoadDataXml(XmlReader reader)
        {
            if (reader == null) throw new ArgumentNullException("reader");
            Boolean consumed = false;
            if (reader.NodeType == XmlNodeType.Element)
            {
                consumed = true;
                using (XmlReader childReader = reader.ReadSubtree())
                {
                    if (childReader.MoveToContent() == XmlNodeType.Element)
                    {
                        if (childReader.MoveToFirstAttribute())
                        {
                            consumed = true;
                            do
                            {
                                //skip the attributes.
                            } while (childReader.MoveToNextAttribute());
                        }
                        childReader.Read();
                        //move through the elements
                        while (childReader.MoveToContent() == XmlNodeType.Element)
                        {
                            //create the object.
                            TElement child = new TElement();
                            if (child != null)
                            {
                                if (!child.LoadDataXml(childReader)) childReader.Skip();
                                //add the child
                                Add(child);
                                if (!reader.Read()) break;
                            }
                            else
                            {
                                reader.Skip();
                            }
                        }
                    }
                }
            }
            return consumed;
        }
        /// <summary>
        /// Writes the entity XML.
        /// </summary>
        /// <param name="writer">The writer.</param>
        internal override void WriteDataXml(XmlWriter writer)
        {
            if (writer == null) throw new ArgumentNullException("writer");
            //write out the child elements
            foreach (TElement item in this)
            {
                writer.WriteStartElement(GetElementName());
                item.WriteDataXml(writer);
                writer.WriteEndElement();
            }
        }
        /// <summary>
        /// Loads the data using the contents of the supplied
        /// <see cref="System.Data.IDataReader"/> and the ColumnName property
        /// of the <see cref="T:Property"/> class to load the entity.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <returns>
        /// 	<c>true</c> if the entity was loaded; <c>false</c> otherwise.
        /// </returns>
        public override bool LoadData(System.Data.IDataReader reader)
        {
            Boolean loaded = false;
            if (reader != null)
            {
                while (true)
                {
                    TElement entity = new TElement();
                    Boolean consumed = entity.LoadData(reader);
                    if (!consumed) break;
                    Add(entity);
                    loaded = true;
                }
            }
            IsNew = !loaded;
            return loaded;
        }
        /// <summary>
        /// Gets the name of the element.
        /// </summary>
        /// <returns></returns>
        internal protected override String GetElementName()
        {
            return typeof(TElement).Name;
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public override object Clone()
        {
            DomainDataCollection<TElement> cloned = base.Clone() as DomainDataCollection<TElement>;
            foreach (TElement item in this)
            {
                TElement child = item.Clone() as TElement;
                cloned.Add(child);
            }
            return cloned;
        }
        #endregion

        #region IList<TElement> Members

        /// <summary>
        /// Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1"></see>.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1"></see>.</param>
        /// <returns>
        /// The index of item if found in the list; otherwise, -1.
        /// </returns>
        public int IndexOf(TElement item)
        {
            return _list.IndexOf(item);
        }

        /// <summary>
        /// Inserts an item to the <see cref="T:System.Collections.Generic.IList`1"></see> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which item should be inserted.</param>
        /// <param name="item">The object to insert into the <see cref="T:System.Collections.Generic.IList`1"></see>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"></see> is read-only.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"></see>.</exception>
        public void Insert(int index, TElement item)
        {
            _list.Insert(index, item);
            IsDirty = true;
        }

        /// <summary>
        /// Removes the <see cref="T:System.Collections.Generic.IList`1"></see> item at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index of the item to remove.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"></see> is read-only.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"></see>.</exception>
        public void RemoveAt(int index)
        {
            _list.RemoveAt(index);
            IsDirty = true;
        }

        #endregion

        #region ICollection<TElement> Members

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        public void Add(TElement item)
        {
            if (item == null) throw new ArgumentNullException("item");
            item.Parent = this;
            _list.Add(item);
            IsDirty = true;
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only. </exception>
        public void Clear()
        {
            _list.Clear();
            IsDirty = true;
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> contains a specific value.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <returns>
        /// true if item is found in the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false.
        /// </returns>
        public bool Contains(TElement item)
        {
            if (item == null) throw new ArgumentNullException("item");
            return _list.Contains(item);
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">arrayIndex is less than 0.</exception>
        /// <exception cref="T:System.ArgumentNullException">array is null.</exception>
        /// <exception cref="T:System.ArgumentException">array is multidimensional.-or-arrayIndex is equal to or greater than the length of array.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"></see> is greater than the available space from arrayIndex to the end of the destination array.-or-Type T cannot be cast automatically to the type of the destination array.</exception>
        public void CopyTo(TElement[] array, int arrayIndex)
        {
            if (array == null) throw new ArgumentNullException("array");
            _list.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</returns>
        public int Count
        {
            get { return _list.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only; otherwise, false.</returns>
        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"></see>.</param>
        /// <returns>
        /// true if item was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"></see>; otherwise, false. This method also returns false if item is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"></see>.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.</exception>
        public bool Remove(TElement item)
        {
            IsDirty = true;
            return _list.Remove(item);
        }

        #endregion

        #region IEnumerable<TElement> Members

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"></see> that can be used to iterate through the collection.
        /// </returns>
        public IEnumerator<TElement> GetEnumerator()
        {
            return _list.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
        /// </returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        #region IListSource Members

        /// <summary>
        /// Gets a value indicating whether the collection is a collection of <see cref="T:System.Collections.IList"></see> objects.
        /// </summary>
        /// <value></value>
        /// <returns>true if the collection is a collection of <see cref="T:System.Collections.IList"></see> objects; otherwise, false.</returns>
        bool IListSource.ContainsListCollection
        {
            get { return false; }
        }

        /// <summary>
        /// Returns an <see cref="T:System.Collections.IList"></see> that can be bound to a data source from an object that does not implement an <see cref="T:System.Collections.IList"></see> itself.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IList"></see> that can be bound to a data source from the object.
        /// </returns>
        System.Collections.IList IListSource.GetList()
        {
            return _list;
        }

        #endregion
    }
    #endregion

    #region Custom List collection
    /// <summary>
    /// Custom <see cref="T:List<>"/> collection that supports data binding
    /// and initializes the <see cref="T:BusinessEntity"/> instance's <c>Parent</c>
    /// property before adding to the collection.
    /// </summary>
    /// <typeparam name="TElement"></typeparam>
    [Serializable]
    public sealed class DomainDataList<TElement> : List<TElement>, System.Collections.IList where TElement : DomainData, new()
    {
        #region Fields
        private DomainData _parent;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="DomainDataList&lt;TElement&gt;"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        public DomainDataList(DomainData parent)
        {
            _parent = parent;
        }
        #endregion

        #region IList<TElement> Members

        /// <summary>
        /// Inserts an element into the <see cref="T:System.Collections.Generic.List`1"></see> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which item should be inserted.</param>
        /// <param name="item">The object to insert. The value can be null for reference types.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is less than 0.-or-index is greater than <see cref="P:System.Collections.Generic.List`1.Count"></see>.</exception>
        public new void Insert(int index, TElement item)
        {
            item.Parent = _parent;
            base.Insert(index, item);
        }
        #endregion

        #region ICollection<TElement> Members

        /// <summary>
        /// Adds an object to the end of the <see cref="T:System.Collections.Generic.List`1"></see>.
        /// </summary>
        /// <param name="item">The object to be added to the end of the <see cref="T:System.Collections.Generic.List`1"></see>. The value can be null for reference types.</param>
        public new void Add(TElement item)
        {
            item.Parent = _parent;
            base.Add(item);
        }

        #endregion

        #region IList Members

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.IList"></see>.
        /// </summary>
        /// <param name="value">The <see cref="T:System.Object"></see> to add to the <see cref="T:System.Collections.IList"></see>.</param>
        /// <returns>
        /// The position into which the new element was inserted.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
        int System.Collections.IList.Add(object value)
        {
            Add((TElement)value);
            return Count - 1;
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.IList"></see> contains a specific value.
        /// </summary>
        /// <param name="value">The <see cref="T:System.Object"></see> to locate in the <see cref="T:System.Collections.IList"></see>.</param>
        /// <returns>
        /// true if the <see cref="T:System.Object"></see> is found in the <see cref="T:System.Collections.IList"></see>; otherwise, false.
        /// </returns>
        bool System.Collections.IList.Contains(object value)
        {
            return Contains((TElement)value);
        }

        /// <summary>
        /// Determines the index of a specific item in the <see cref="T:System.Collections.IList"></see>.
        /// </summary>
        /// <param name="value">The <see cref="T:System.Object"></see> to locate in the <see cref="T:System.Collections.IList"></see>.</param>
        /// <returns>
        /// The index of value if found in the list; otherwise, -1.
        /// </returns>
        int System.Collections.IList.IndexOf(object value)
        {
            return IndexOf((TElement)value);
        }

        /// <summary>
        /// Inserts an item to the <see cref="T:System.Collections.IList"></see> at the specified index.
        /// </summary>
        /// <param name="index">The zero-based index at which value should be inserted.</param>
        /// <param name="value">The <see cref="T:System.Object"></see> to insert into the <see cref="T:System.Collections.IList"></see>.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.IList"></see>. </exception>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
        /// <exception cref="T:System.NullReferenceException">value is null reference in the <see cref="T:System.Collections.IList"></see>.</exception>
        void System.Collections.IList.Insert(int index, object value)
        {
            Insert(index, (TElement)value);
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.IList"></see> has a fixed size.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.IList"></see> has a fixed size; otherwise, false.</returns>
        bool System.Collections.IList.IsFixedSize
        {
            get { return false; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"></see> is read-only; otherwise, false.</returns>
        bool System.Collections.IList.IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.IList"></see>.
        /// </summary>
        /// <param name="value">The <see cref="T:System.Object"></see> to remove from the <see cref="T:System.Collections.IList"></see>.</param>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
        void System.Collections.IList.Remove(object value)
        {
            Remove((TElement)value);
        }

        /// <summary>
        /// Gets or sets the <see cref="System.Object"/> at the specified index.
        /// </summary>
        /// <value></value>
        object System.Collections.IList.this[int index]
        {
            get
            {
                return this[index];
            }
            set
            {
                this[index] = (TElement)value;
            }
        }

        #endregion

        #region ICollection Members

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.ICollection"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.ICollection"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        /// <exception cref="T:System.ArgumentNullException">array is null. </exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">index is less than zero. </exception>
        /// <exception cref="T:System.ArgumentException">array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source <see cref="T:System.Collections.ICollection"></see> is greater than the available space from index to the end of the destination array. </exception>
        /// <exception cref="T:System.InvalidCastException">The type of the source <see cref="T:System.Collections.ICollection"></see> cannot be cast automatically to the type of the destination array. </exception>
        void System.Collections.ICollection.CopyTo(Array array, int index)
        {
            CopyTo((TElement[])array, index);
        }

        /// <summary>
        /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"></see> is synchronized (thread safe).
        /// </summary>
        /// <value></value>
        /// <returns>true if access to the <see cref="T:System.Collections.ICollection"></see> is synchronized (thread safe); otherwise, false.</returns>
        bool System.Collections.ICollection.IsSynchronized
        {
            get { return false; }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"></see>.
        /// </summary>
        /// <value></value>
        /// <returns>An object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"></see>.</returns>
        object System.Collections.ICollection.SyncRoot
        {
            get { return this; }
        }

        #endregion
    }
    #endregion

    #endregion
}
